home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / math / ast53src.zip / XGENERAL.C < prev    next >
C/C++ Source or Header  |  1996-09-29  |  27KB  |  964 lines

  1. /*
  2. ** Astrolog (Version 5.30) File: xgeneral.c
  3. **
  4. ** IMPORTANT NOTICE: The graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1996 by Walter D. Pullen
  6. ** (Astara@msn.com, http://www.magitech.com/~cruiser1/astrolog.htm).
  7. ** Permission is granted to freely use and distribute these routines
  8. ** provided one doesn't sell, restrict, or profit from them in any way.
  9. ** Modification is allowed provided these notices remain with any
  10. ** altered or edited versions of the program.
  11. **
  12. ** The main planetary calculation routines used in this program have
  13. ** been Copyrighted and the core of this program is basically a
  14. ** conversion to C of the routines created by James Neely as listed in
  15. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  16. ** available from Matrix Software. The copyright gives us permission to
  17. ** use the routines for personal use but not to sell them or profit from
  18. ** them in any way.
  19. **
  20. ** The PostScript code within the core graphics routines are programmed
  21. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  22. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  23. **
  24. ** The extended accurate ephemeris databases and formulas are from the
  25. ** calculation routines in the program "Placalc" and are programmed and
  26. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  27. ** (alois@azur.ch). The use of that source code is subject to
  28. ** regulations made by Astrodienst Zurich, and the code is not in the
  29. ** public domain. This copyright notice must not be changed or removed
  30. ** by any user of this program.
  31. **
  32. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  33. ** X Window graphics initially programmed 10/23-29/1991.
  34. ** PostScript graphics initially programmed 11/29-30/1992.
  35. ** Last code change made 9/22/1996.
  36. */
  37.  
  38. #include "astrolog.h"
  39.  
  40.  
  41. #ifdef GRAPH
  42. /*
  43. ******************************************************************************
  44. ** Core Graphic Procedures.
  45. ******************************************************************************
  46. */
  47.  
  48. /* Set the current color to use in drawing on the screen or bitmap array. */
  49.  
  50. void DrawColor(col)
  51. KI col;
  52. {
  53. #ifdef WIN
  54.   HPEN hpenT;
  55. #endif
  56. #ifdef MACG
  57.   RGBColor kv;
  58. #endif
  59.  
  60.   if (gi.fFile) {
  61. #ifdef PS
  62.     if (gs.fPS) {
  63.       if (gi.kiCur != col) {
  64.         PsStrokeForce();      /* Render existing path with current color */
  65.         fprintf(gi.file, "%.2f %.2f %.2f c\n",
  66.           (real)RGBR(rgbbmp[col])/255.0, (real)RGBG(rgbbmp[col])/255.0,
  67.           (real)RGBB(rgbbmp[col])/255.0);
  68.       }
  69.     }
  70. #endif
  71. #ifdef META
  72.     if (gs.fMeta)
  73.       gi.kiLineDes = col;
  74. #endif
  75.   }
  76. #ifdef X11
  77.   else
  78.     XSetForeground(gi.disp, gi.gc, rgbind[col]);
  79. #endif
  80. #ifdef WIN
  81.   else {
  82.     if (gi.kiCur != col) {
  83.       hpenT = wi.hpen;
  84.       wi.hpen = CreatePen(PS_SOLID, gi.nScaleT, (COLORREF)rgbbmp[col]);
  85.       SelectObject(wi.hdc, wi.hpen);
  86.       if (hpenT != (HPEN)NULL)
  87.         DeleteObject(hpenT);
  88.     }
  89.   }
  90. #endif
  91. #ifdef MSG
  92.   else
  93.     _setcolor(col);
  94. #endif
  95. #ifdef BGI
  96.   else
  97.     setcolor(col);
  98. #endif
  99. #ifdef MACG
  100.   else {
  101.     kv.red   = RGBR(rgbbmp[col]) << 8;
  102.     kv.green = RGBG(rgbbmp[col]) << 8;
  103.     kv.blue  = RGBB(rgbbmp[col]) << 8;
  104.     RGBForeColor(&kv);
  105.     RGBBackColor(&kv);
  106.   }
  107. #endif
  108.   gi.kiCur = col;
  109. }
  110.  
  111.  
  112. /* Set a single point on the screen. This is the most basic graphic function */
  113. /* and is called by all the more complex routines. Based on what mode we are */
  114. /* in, we either set a cell in the bitmap array or a pixel on the window.    */
  115.  
  116. void DrawPoint(x, y)
  117. int x, y;
  118. {
  119.   if (gi.fFile) {
  120.     if (gs.fBitmap) {
  121.       /* Force the coordinates to be within the bounds of the bitmap array. */
  122.  
  123.       if (x < 0)
  124.         x = 0;
  125.       else if (x >= gs.xWin)
  126.         x = gs.xWin-1;
  127.       if (y < 0)
  128.         y = 0;
  129.       else if (y >= gs.yWin)
  130.         y = gs.yWin-1;
  131.       if (gi.yBand) {
  132.         y -= gi.yOffset;
  133.         if (y < 0 || y >= gi.yBand)
  134.           return;
  135.       }
  136.       BmSet(gi.bm, x, y, gi.kiCur);
  137.     }
  138. #ifdef PS
  139.     else if (gs.fPS) {
  140.       DrawColor(gi.kiCur);
  141.       PsLineCap(fTrue);
  142.       fprintf(gi.file, "%d %d d\n", x, y);
  143.       PsStroke(2);
  144.     }
  145. #endif
  146. #ifdef META
  147.     else {
  148.       gi.kiFillDes = gi.kiCur;
  149.       MetaSelect();
  150.       MetaEllipse(x-gi.nPenWid/2, y-gi.nPenWid/2,
  151.         x+gi.nPenWid/2, y+gi.nPenWid/2);
  152.     }
  153. #endif
  154.   }
  155. #ifdef X11
  156.   else
  157.     XDrawPoint(gi.disp, gi.pmap, gi.gc, x, y);
  158. #endif
  159. #ifdef WIN
  160.   else {
  161.     if (wi.hdcPrint == hdcNil)
  162.       SetPixel(wi.hdc, x, y, (COLORREF)rgbbmp[gi.kiCur]);
  163.     else {
  164.       MoveTo(wi.hdc, x, y);
  165.       LineTo(wi.hdc, x, y);
  166.     }
  167.   }
  168. #endif
  169. #ifdef MSG
  170.   else
  171.     _setpixel(gi.xOffset + x, gi.yOffset + y);
  172. #endif
  173. #ifdef BGI
  174.   else
  175.     putpixel(gi.xOffset + x, gi.yOffset + y, gi.kiCur);
  176. #endif
  177. #ifdef MACG
  178.   else {
  179.     MoveTo(x, y); LineTo(x, y);
  180.   }
  181. #endif
  182. }
  183.  
  184.  
  185. /* Draw dot a little larger than just a single pixel at specified location. */
  186.  
  187. void DrawSpot(x, y)
  188. int x, y;
  189. {
  190. #ifdef PS
  191.   if (gs.fPS) {
  192.     PsLineWidth((int)(gi.rLineWid*3.0));
  193.     DrawPoint(x, y);
  194.     PsLineWidth((int)(gi.rLineWid/3.0));
  195.     return;
  196.   }
  197. #endif
  198. #ifdef META
  199.   if (gs.fMeta) {
  200.     gi.kiFillDes = gi.kiCur;
  201.     MetaSelect();
  202.     MetaEllipse(x-gi.nPenWid, y-gi.nPenWid, x+gi.nPenWid, y+gi.nPenWid);
  203.     return;
  204.   }
  205. #endif
  206.   DrawPoint(x, y);
  207.   DrawPoint(x, y-1);
  208.   DrawPoint(x-1, y);
  209.   DrawPoint(x+1, y);
  210.   DrawPoint(x, y+1);
  211. }
  212.  
  213.  
  214. /* Draw a filled in block, defined by the corners of its rectangle. */
  215.  
  216. void DrawBlock(x1, y1, x2, y2)
  217. int x1, y1, x2, y2;
  218. {
  219.   int x, y;
  220. #ifdef MACG
  221.   Rect rc;
  222. #endif
  223.  
  224.   if (gi.fFile) {
  225.     if (gs.fBitmap) {
  226.       /* Force the coordinates to be within the bounds of the bitmap band. */
  227.  
  228.       if (x1 < 0)
  229.         x1 = 0;
  230.       if (x2 >= gs.xWin)
  231.         x2 = gs.xWin-1;
  232.       if (gi.yBand) {
  233.         y1 -= gi.yOffset;
  234.         if (y1 < 0)
  235.           y1 = 0;
  236.         y2 -= gi.yOffset;
  237.         if (y2 >= gi.yBand)
  238.           y2 = gi.yBand-1;
  239.       }
  240.       for (y = y1; y <= y2; y++)           /* For bitmap, we have to  */
  241.         for (x = x1; x <= x2; x++)         /* just fill in the array. */
  242.           BmSet(gi.bm, x, y, gi.kiCur);
  243.     }
  244. #ifdef PS
  245.     else if (gs.fPS) {
  246.       DrawColor(gi.kiCur);
  247.       fprintf(gi.file, "%d %d %d %d rf\n",
  248.         Max(x1-gi.nPenWid/4, 0), Max(y1-gi.nPenWid/4, 0),
  249.         x2-x1+gi.nPenWid/4, y2-y1+gi.nPenWid/4);
  250.     }
  251. #endif
  252. #ifdef META
  253.     else {
  254.       gi.kiFillDes = gi.kiCur;
  255.       MetaSelect();
  256.       MetaRectangle(x1-gi.nPenWid/2, y1-gi.nPenWid/2,
  257.         x2+gi.nPenWid/2, y2+gi.nPenWid/2);
  258.     }
  259. #endif
  260.   }
  261. #ifdef X11
  262.   else
  263.     XFillRectangle(gi.disp, gi.pmap, gi.gc, x1, y1, x2-x1, y2-y1);
  264. #endif
  265. #ifdef WIN
  266.   else {
  267.     wi.hbrush = CreateSolidBrush((COLORREF)rgbbmp[gi.kiCur]);
  268.     SelectObject(wi.hdc, wi.hbrush);
  269.     PatBlt(wi.hdc, x1, y1, x2-x1 + 1, y2-y1 + 1, PATCOPY);
  270.     SelectObject(wi.hdc, GetStockObject(NULL_BRUSH));
  271.     DeleteObject(wi.hbrush);
  272.   }
  273. #endif
  274. #ifdef MSG
  275.   else
  276.     _rectangle(_GFILLINTERIOR,
  277.       gi.xOffset + x1, gi.yOffset + y1, gi.xOffset + x2, gi.yOffset + y2);
  278. #endif
  279. #ifdef BGI
  280.   else {
  281.     setfillstyle(SOLID_FILL, gi.kiCur);
  282.     bar(gi.xOffset + x1, gi.yOffset + y1, gi.xOffset + x2, gi.yOffset + y2);
  283.   }
  284. #endif
  285. #ifdef MACG
  286.   else {
  287.     SetRect(&rc, x1, y1, x2+1, y2+1);
  288.     EraseRect(&rc);
  289.   }
  290. #endif
  291. }
  292.  
  293.  
  294. /* Draw a rectangle on the screen with specified thickness. This is just   */
  295. /* like DrawBlock() except that we are only drawing the edges of the area. */
  296.  
  297. void DrawBox(x1, y1, x2, y2, xsiz, ysiz)
  298. int x1, y1, x2, y2, xsiz, ysiz;
  299. {
  300. #ifdef META
  301.   if (gs.fMeta)
  302.     /* For thin boxes in metafiles, we can just output one rectangle record */
  303.     /* instead of drawing each side separately as we have to do otherwise.  */
  304.     if (xsiz <= 1 && ysiz <= 1) {
  305.       gi.kiFillDes = kNull;          /* Specify a hollow fill brush. */
  306.       MetaSelect();
  307.       MetaRectangle(x1, y1, x2, y2);
  308.       return;
  309.     }
  310. #endif
  311.   DrawBlock(x1, y1, x2, y1 + ysiz - 1);
  312.   DrawBlock(x1, y1 + ysiz, x1 + xsiz - 1, y2 - ysiz);
  313.   DrawBlock(x2 - xsiz + 1, y1 + ysiz, x2, y2 - ysiz);
  314.   DrawBlock(x1, y2 - ysiz + 1, x2, y2);
  315. }
  316.  
  317.  
  318. /* Clear and erase the graphics screen or bitmap contents. */
  319.  
  320. void DrawClearScreen()
  321. {
  322. #ifdef PS
  323.   if (gs.fPS) {
  324.     /* For PostScript charts first output page orientation information. */
  325.     if (!gi.fEps) {
  326.       if (gs.nOrient == 0)
  327.         gs.nOrient = gs.xWin > gs.yWin ? -1 : 1;
  328.       if (gs.nOrient < 0) {
  329.         /* chartx and charty are reversed for Landscape mode. */
  330.         fprintf(gi.file, "%d %d translate\n",
  331.           ((int)(gs.xInch*72.0+rRound) + gs.yWin)/2,
  332.           ((int)(gs.yInch*72.0+rRound) + gs.xWin)/2);
  333.         fprintf(gi.file, "-90 rotate\n");
  334.       } else {
  335.         /* Most charts are in Portrait mode */
  336.         fprintf(gi.file, "%d %d translate\n",
  337.           ((int)(gs.xInch*72.0+rRound) - gs.xWin)/2,
  338.           ((int)(gs.yInch*72.0+rRound) + gs.yWin)/2);
  339.       }
  340.     } else
  341.       fprintf(gi.file, "0 %d translate\n", gs.yWin);
  342.     fprintf(gi.file, "1 -1 scale\n");
  343.     gs.nScale *= PSMUL; gs.xWin *= PSMUL; gs.yWin *= PSMUL; gi.nScale *= PSMUL;
  344.     fprintf(gi.file, "1 %d div dup scale\n", PSMUL);
  345.   }
  346. #endif
  347. #ifdef META
  348.   if (gs.fMeta)
  349.     MetaInit();    /* For metafiles first go write our header information. */
  350. #endif
  351.  
  352.   /* Don't actually erase the screen if the -Xj switch is in effect. */
  353.   if (gs.fJetTrail)
  354.     return;
  355.  
  356. #ifdef MSG
  357.   if (!gi.fFile)
  358.     _clearscreen(_GCLEARSCREEN);
  359. #endif
  360. #ifdef BGI
  361.   if (!gi.fFile)
  362.     clearviewport();
  363. #endif
  364.   DrawColor(gi.kiOff);
  365.   DrawBlock(0, 0, gs.xWin - 1, gs.yWin - 1);    /* Clear bitmap screen. */
  366. }
  367.  
  368.  
  369. /* Draw a line on the screen, specified by its endpoints. In addition, we */
  370. /* have specified a skip factor, which allows us to draw dashed lines.    */
  371.  
  372. void DrawDash(x1, y1, x2, y2, skip)
  373. int x1, y1, x2, y2, skip;
  374. {
  375.   int x = x1, y = y1, xadd, yadd, yinc, xabs, yabs, i, j = 0;
  376.  
  377.   if (skip < 0)
  378.     skip = 0;
  379. #ifdef ISG
  380.   if (!gi.fFile) {
  381.     if (!skip) {
  382. #ifdef X11
  383.       /* For non-dashed X window lines, let's have the Xlib do it for us. */
  384.  
  385.       XDrawLine(gi.disp, gi.pmap, gi.gc, x1, y1, x2, y2);
  386. #endif
  387. #ifdef WIN
  388.       /* For Windows lines, we have to manually draw the last pixel. */
  389.  
  390.       MoveTo(wi.hdc, x1, y1);
  391.       LineTo(wi.hdc, x2, y2);
  392.       SetPixel(wi.hdc, x2, y2, (COLORREF)rgbbmp[gi.kiCur]);
  393. #endif
  394. #ifdef PCG
  395.       /* For non-dashed lines, let's have the graphics library do it for us. */
  396.  
  397.       PcMoveTo(gi.xOffset + x1, gi.yOffset + y1);
  398.       PcLineTo(gi.xOffset + x2, gi.yOffset + y2);
  399. #endif
  400. #ifdef MACG
  401.       MoveTo(x1, y1);
  402.       LineTo(x2, y2);
  403. #endif
  404.       return;
  405.     }
  406. #ifdef WIN
  407.     if (skip && wi.hdcPrint != hdcNil)
  408.       skip = (skip + 1)*METAMUL - 1;
  409. #endif
  410.   }
  411. #endif /* ISG */
  412.  
  413. #ifdef PS
  414.   if (gs.fPS) {
  415.  
  416.     /* For PostScript charts we can save file size if we output a LineTo  */
  417.     /* command when the start vertex is the same as the end vertex of the */
  418.     /* previous line drawn, instead of writing out both vertices.         */
  419.  
  420.     PsLineCap(fTrue);
  421.     PsDash(skip);
  422.     if (gi.xPen != x1 || gi.yPen != y1)
  423.       fprintf(gi.file, "%d %d %d %d l\n", x1, y1, x2, y2);
  424.     else
  425.       fprintf(gi.file, "%d %d t\n", x2, y2);
  426.     gi.xPen = x2; gi.yPen = y2;
  427.     PsStroke(2);
  428.     return;
  429.   }
  430. #endif
  431.  
  432. #ifdef META
  433.   if (gs.fMeta) {
  434.  
  435.     /* For metafile charts we can really save file size for consecutive */
  436.     /* lines sharing endpoints by consolidating them into a PolyLine.   */
  437.  
  438.     if (gi.xPen != x1 || gi.yPen != y1) {
  439.       gi.kiLineDes = (gi.kiLineDes & 15) + 16*(skip > 3 ? 3 : skip);
  440.       MetaSelect();
  441.       gi.pwPoly = gi.pwMetaCur;
  442.       MetaRecord(8, 0x325);      /* Polyline */
  443.       MetaWord(2); MetaWord(x1); MetaWord(y1);
  444.     } else {
  445.       *gi.pwPoly += 2;
  446.       (*(gi.pwPoly+3))++;
  447.       /* Note: We should technically update the max record size in the   */
  448.       /* file header if need be here too, but it doesn't seem necessary. */
  449.     }
  450.     MetaWord(x2); MetaWord(y2);
  451.     gi.xPen = x2; gi.yPen = y2;
  452.     return;
  453.   }
  454. #endif
  455.  
  456.   /* If none of the above cases hold, we have to draw the line dot by dot. */
  457.  
  458.   xadd = x2 - x1 >= 0 ? 1 : 3;
  459.   yadd = y2 - y1 >= 0 ? 2 : 4;
  460.   xabs = abs(x2 - x1);
  461.   yabs = abs(y2 - y1);
  462.  
  463.   /* Technically what we're doing here is drawing a line which is more    */
  464.   /* horizontal then vertical. We always increment x by 1, and increment  */
  465.   /* y whenever a fractional variable passes a certain amount. For lines  */
  466.   /* that are more vertical than horizontal, we just swap x and y coords. */
  467.  
  468.   if (xabs < yabs) {
  469.     SwapN(xadd, yadd);
  470.     SwapN(xabs, yabs);
  471.   }
  472.   yinc = (xabs >> 1) - ((xabs & 1 ^ 1) && xadd > 2);
  473.   for (i = xabs + 1; i; i--) {
  474.     if (j < 1)
  475.       DrawPoint(x, y);
  476.     j = j < skip ? j+1 : 0;
  477.     switch (xadd) {
  478.     case 1: x++; break;
  479.     case 2: y++; break;
  480.     case 3: x--; break;
  481.     case 4: y--; break;
  482.     }
  483.     yinc += yabs;
  484.     if (yinc - xabs >= 0) {
  485.       yinc -= xabs;
  486.       switch (yadd) {
  487.       case 1: x++; break;
  488.       case 2: y++; break;
  489.       case 3: x--; break;
  490.       case 4: y--; break;
  491.       }
  492.     }
  493.   }
  494. }
  495.  
  496.  
  497. /* Draw a normal line on the screen; however, if the x coordinates are close */
  498. /* to either of the two given bounds, then we assume that the line runs off  */
  499. /* one side and reappears on the other, so draw the appropriate two lines    */
  500. /* instead. This is used by the Ley line and astro-graph routines, which     */
  501. /* draw lines running around the world and hence off the edges of the maps.  */
  502.  
  503. void DrawWrap(x1, y1, x2, y2, xmin, xmax)
  504. int x1, y1, x2, y2, xmin, xmax;
  505. {
  506.   int xmid, ymid, i, j, k;
  507.  
  508.   if (x1 < 0) {           /* Special case for drawing world map. */
  509.     DrawPoint(x2, y2);
  510.     return;
  511.   }
  512.   j = (xmin < 0);    /* Negative xmin means always draw forward. */
  513.   if (j)
  514.     neg(xmin);
  515.   xmid = (xmax-xmin) / 2;
  516.   if (j)
  517.     k = (x1 < x2 ? xmid*7 : xmid)/4;
  518.   else
  519.     k = xmid;
  520.  
  521.   /* If endpoints aren't near opposite edges, just draw the line and return. */
  522.  
  523.   if (abs(x2-x1) < k-xmin) {
  524.     DrawLine(x1, y1, x2, y2);
  525.     return;
  526.   }
  527.   if ((i = (xmax-xmin+1) + (!j && x1 < xmid ? x1-x2 : x2-x1)) == 0)
  528.     i = 1;
  529.  
  530.   /* Determine vertical coordinate where our line runs off edges of screen. */
  531.  
  532.   ymid = y1+(int)((real)(y2-y1)*
  533.     (!j && x1 < xmid ? (real)(x1-xmin) : (real)(xmax-x1))/(real)i + rRound);
  534.   DrawLine(x1, y1, !j && x1 < xmid ? xmin : xmax, ymid);
  535.   DrawLine(j || x2 < xmid ? xmin : xmax, ymid, x2, y2);
  536. }
  537.  
  538.  
  539. /* This routine, and its companion below, clips a line defined by its  */
  540. /* endpoints to either above some line y=c, or below some line y=c. By */
  541. /* passing in parameters in different orders, we can clip to vertical  */
  542. /* lines, too. These are used by the DrawClip() routine below.         */
  543.  
  544. void ClipLesser(x1, y1, x2, y2, s)
  545. int *x1, *y1, *x2, *y2, s;
  546. {
  547.   *x1 -= (int)((long)(*y1-s)*(*x2-*x1)/(*y2-*y1));
  548.   *y1 = s;
  549. }
  550.  
  551. void ClipGreater(x1, y1, x2, y2, s)
  552. int *x1, *y1, *x2, *y2, s;
  553. {
  554.   *x1 += (int)((long)(s-*y1)*(*x2-*x1)/(*y2-*y1));
  555.   *y1 = s;
  556. }
  557.  
  558.  
  559. /* Draw a line on the screen. This is just like DrawLine() routine earlier; */
  560. /* however, first clip the endpoints to the given viewport before drawing.  */
  561.  
  562. void DrawClip(x1, y1, x2, y2, xl, yl, xh, yh, skip)
  563. int x1, y1, x2, y2, xl, yl, xh, yh, skip;
  564. {
  565.   if (x1 < xl)
  566.     ClipLesser (&y1, &x1, &y2, &x2, xl);    /* Check left side of window. */
  567.   if (x2 < xl)
  568.     ClipLesser (&y2, &x2, &y1, &x1, xl);
  569.   if (y1 < yl)
  570.     ClipLesser (&x1, &y1, &x2, &y2, yl);    /* Check top side of window.  */
  571.   if (y2 < yl)
  572.     ClipLesser (&x2, &y2, &x1, &y1, yl);
  573.   if (x1 > xh)
  574.     ClipGreater(&y1, &x1, &y2, &x2, xh);    /* Check right of window.  */
  575.   if (x2 > xh)
  576.     ClipGreater(&y2, &x2, &y1, &x1, xh);
  577.   if (y1 > yh)
  578.     ClipGreater(&x1, &y1, &x2, &y2, yh);    /* Check bottom of window. */
  579.   if (y2 > yh)
  580.     ClipGreater(&x2, &y2, &x1, &y1, yh);
  581.   DrawDash(x1, y1, x2, y2, skip);           /* Go draw the line.       */
  582. }
  583.  
  584.  
  585. /* Draw a circle or ellipse inside the given bounding rectangle. */
  586.  
  587. void DrawEllipse(x1, y1, x2, y2)
  588. int x1, y1, x2, y2;
  589. {
  590.   int x, y, rx, ry, m, n, u, v, i;
  591. #ifdef MACG
  592.   Rect rc;
  593. #endif
  594.  
  595.   if (gi.fFile) {
  596.     x = (x1+x2)/2; y = (y1+y2)/2; rx = (x2-x1)/2; ry = (y2-y1)/2;
  597.     if (gs.fBitmap) {
  598.       m = x + rx; n = y;
  599.       for (i = 0; i <= nDegMax; i += DEGINC) {
  600.         u = x + (int)(((real)rx+rRound)*RCosD((real)i));
  601.         v = y + (int)(((real)ry+rRound)*RSinD((real)i));
  602.         DrawLine(m, n, u, v);
  603.         m = u; n = v;
  604.       }
  605.     }
  606. #ifdef PS
  607.     else if (gs.fPS) {
  608.       PsLineCap(fFalse);
  609.       PsStrokeForce();
  610.       PsDash(0);
  611.       fprintf(gi.file, "%d %d %d %d el\n", rx, ry, x, y);
  612.     }
  613. #endif
  614. #ifdef META
  615.     else {
  616.       gi.kiFillDes = kNull;    /* Specify a hollow fill brush. */
  617.       MetaSelect();
  618.       MetaEllipse(x1+gi.nPenWid/3, y1+gi.nPenWid/3,
  619.         x2+gi.nPenWid/3, y2+gi.nPenWid/3);
  620.     }
  621. #endif
  622.   }
  623. #ifdef X11
  624.   else
  625.     XDrawArc(gi.disp, gi.pmap, gi.gc, x1, y1, x2-x1, y2-y1, 0, nDegMax*64);
  626. #endif
  627. #ifdef WIN
  628.   else
  629.     Ellipse(wi.hdc, x1, y1, x2, y2);
  630. #endif
  631. #ifdef MSG
  632.   else
  633.     _ellipse(_GBORDER, gi.xOffset + x1, gi.yOffset + y1,
  634.       gi.xOffset + x2, gi.yOffset + y2);
  635. #endif
  636. #ifdef BGI
  637.   else
  638.     ellipse(gi.xOffset + (x1+x2)/2, gi.yOffset + (y1+y2)/2,
  639.       0, 360, (x2-x1)/2, (y2-y1)/2);
  640. #endif
  641. #ifdef MACG
  642.   else {
  643.     SetRect(&rc, x1, y1, x2, y2);
  644.     FrameOval(&rc);
  645.   }
  646. #endif
  647. }
  648.  
  649.  
  650. /* Print a string of text on the graphic window at specified location. To  */
  651. /* do this we either use Astrolog's own "font" (6x10) and draw each letter */
  652. /* separately, or else specify system fonts for PostScript and metafiles.  */
  653.  
  654. void DrawSz(sz, x, y, dt)
  655. CONST char *sz;
  656. int x, y, dt;
  657. {
  658.   int s = gi.nScale, c = gi.kiCur, cch, i;
  659.  
  660.   cch = CchSz(sz);
  661.   if (!(dt & dtScale))
  662.     gi.nScale = gi.nScaleT;
  663.   x += gi.nScale;
  664.   if (!(dt & dtLeft))
  665.     x -= cch*xFont*gi.nScale/2;
  666.   if (dt & dtBottom)
  667.     y -= (yFont-3)*gi.nScale;
  668.   else if (!(dt & dtTop))
  669.     y -= yFont*gi.nScale/2;
  670.   if (dt & dtErase) {
  671.     DrawColor(gi.kiOff);
  672.     DrawBlock(x, y, x+xFont*gi.nScale*cch-1, y+(yFont-2)*gi.nScale);
  673.   }
  674.   DrawColor(c);
  675. #ifdef PS
  676.   if (gs.fPS && gs.fFont) {
  677.     PsFont(4);
  678.     fprintf(gi.file, "%d %d(%s)center\n",
  679.       x + xFont*gi.nScale*cch/2, y + yFont*gi.nScale/2, sz);
  680.     gi.nScale = s;
  681.     return;
  682.   }
  683. #endif
  684.   while (*sz) {
  685. #ifdef META
  686.     if (gs.fMeta && gs.fFont) {
  687.       gi.nFontDes = 3;
  688.       gi.kiTextDes = gi.kiCur;
  689.       gi.nAlignDes = 0x6 | 0 /* Center | Top */;
  690.       MetaSelect();
  691.       MetaTextOut(x, y, 1);
  692.       MetaWord(WFromBB(*sz, 0));
  693.     } else
  694. #endif
  695.     {
  696.       i = (_char)*sz-' ';
  697.       if (i < 128-32+1)
  698.         DrawTurtle(szDrawCh[i], x, y);
  699.     }
  700.     x += xFont*gi.nScale;
  701.     sz++;
  702.   }
  703.   gi.nScale = s;
  704. }
  705.  
  706.  
  707. /* Draw the glyph of a sign at particular coordinates on the screen.    */
  708. /* To do this we either use Astrolog's turtle vector representation or  */
  709. /* we may specify a system font character for PostScript and metafiles. */
  710.  
  711. void DrawSign(i, x, y)
  712. int i, x, y;
  713. {
  714. #ifdef PS
  715.   if (gs.fPS && gs.fFont) {
  716.     PsFont(1);
  717.     fprintf(gi.file, "%d %d(%c)center\n", x, y, 'A' + i - 1);
  718.     return;
  719.   }
  720. #endif
  721. #ifdef META
  722.   if (gs.fMeta && gs.fFont) {
  723.     gi.nFontDes = 1;
  724.     gi.kiTextDes = gi.kiCur;
  725.     gi.nAlignDes = 0x6 | 0x8 /* Center | Bottom */;
  726.     MetaSelect();
  727.     MetaTextOut(x, y+4*gi.nScale, 1);
  728.     MetaWord(WFromBB('^' + i - 1, 0));
  729.     return;
  730.   }
  731. #endif
  732.   if (i == sCap && gs.nGlyphs/1000 > 1)
  733.     i = cSign+1;
  734.   DrawTurtle(szDrawSign[i], x, y);
  735. }
  736.  
  737.  
  738. /* Draw the number of a house at particular coordinates on the screen. */
  739. /* We either use a turtle vector or write a number in a system font.   */
  740.  
  741. void DrawHouse(i, x, y)
  742. int i, x, y;
  743. {
  744. #ifdef PS
  745.   if (gs.fPS && gs.fFont) {
  746.     PsFont(3);
  747.     fprintf(gi.file, "%d %d(%d)center\n", x, y, i);
  748.     return;
  749.   }
  750. #endif
  751. #ifdef META
  752.   if (gs.fMeta && gs.fFont) {
  753.     gi.nFontDes = 2;
  754.     gi.kiTextDes = gi.kiCur;
  755.     gi.nAlignDes = 0x6 | 0x8 /* Center | Bottom */;
  756.     MetaSelect();
  757.     MetaTextOut(x, y+3*gi.nScale, 1 + (i>9));
  758.     MetaWord(WFromBB(i > 9 ? '1' : '0'+i, i > 9 ? '0'+i-10 : 0));
  759.     return;
  760.   }
  761. #endif
  762.   DrawTurtle(szDrawHouse[i], x, y);
  763. }
  764.  
  765.  
  766. /* Draw the glyph of an object at particular coordinates on the screen. */
  767.  
  768. void DrawObject(obj, x, y)
  769. int obj, x, y;
  770. {
  771.   char szGlyph[4];
  772.   int ich;
  773.  
  774.   if (!gs.fLabel)    /* If we are inhibiting labels, then do nothing. */
  775.     return;
  776.   DrawColor(kObjB[obj]);
  777.   if (obj <= oNorm) {
  778. #ifdef STROKE
  779.     ich = (obj == oSou && fSouthNode ? oNorm+1 : obj);
  780. #endif
  781. #ifdef PS
  782.     if (gs.fPS && gs.fFont == 1 && obj < uranLo && szObjectFont[ich] != ' ') {
  783.       PsFont(2);
  784.       fprintf(gi.file, "%d %d(%c)center\n", x, y, szObjectFont[ich]);
  785.       return;
  786.     }
  787. #endif
  788. #ifdef META
  789.     if (gs.fMeta && gs.fFont == 1 &&
  790.       obj < uranLo && szObjectFont[ich] != ' ') {
  791.       gi.nFontDes = 4;
  792.       gi.kiTextDes = gi.kiCur;
  793.       gi.nAlignDes = 0x6 | 0x8 /* Center | Bottom */;
  794.       MetaSelect();
  795.       MetaTextOut(x, y+5*gi.nScale, 1);
  796.       MetaWord(WFromBB(szObjectFont[ich], 0));
  797.       return;
  798.     }
  799. #endif
  800.     if (obj == oUra) {
  801.       if ((gs.nGlyphs/100)%10 > 1)
  802.         obj = oNorm+1;
  803.     } else if (obj == oPlu) {
  804.       if ((gs.nGlyphs/10)%10 > 1)
  805.         obj = oNorm+2;
  806.     } else if (obj == oLil) {
  807.       if (fSouthNode)
  808.         obj = oNorm+4;
  809.       else if (gs.nGlyphs%10 > 1)
  810.         obj = oNorm+1+gs.nGlyphs%10;
  811.     }
  812.     if ((gi.nScale & 1) || !szDrawObject2[obj][0])
  813.       DrawTurtle(szDrawObject[obj], x, y);
  814.     else {
  815.       gi.nScale >>= 1;
  816.       DrawTurtle(szDrawObject2[obj], x, y); /* Special hi-res object glyphs. */
  817.       gi.nScale <<= 1;
  818.     }
  819.  
  820.   /* Normally we can just go draw the glyph; however, stars don't have */
  821.   /* glyphs, so for these draw their three letter abbreviation.        */
  822.  
  823.   } else {
  824.     sprintf(szGlyph, "%c%c%c", chObj3(obj));
  825. #ifdef CONSTEL
  826.     /* If doing constellations, give a couple stars more correct */
  827.     /* astronomical names.                                       */
  828.  
  829.     if (gs.fConstel) {
  830.       if (obj == oOri)
  831.         sprintf(szGlyph, "Aln");    /* Alnilam, normally "Orion" */
  832.       else if (obj == oAnd)
  833.         sprintf(szGlyph, "M31");    /* M31, normally "Andromeda" */
  834.     }
  835. #endif
  836.     DrawSz(szGlyph, x, y, dtCent);
  837.   }
  838. }
  839.  
  840.  
  841. /* Draw the glyph of an aspect at particular coordinates on the screen. */
  842. /* Again we either use Astrolog's turtle vector or a system Astro font. */
  843.  
  844. void DrawAspect(asp, x, y)
  845. int asp, x, y;
  846. {
  847. #ifdef PS
  848.   if (gs.fPS && gs.fFont == 1 && szAspectFont[asp-1] != ' ') {
  849.     PsFont(2);
  850.     fprintf(gi.file, "%d %d(%s%c)center\n", x, y,
  851.       asp == aSSq || asp == aSes ? "\\" : "", szAspectFont[asp-1]);
  852.     return;
  853.   }
  854. #endif
  855. #ifdef META
  856.   if (gs.fMeta && gs.fFont == 1 && szAspectFont[asp-1] != ' ') {
  857.     gi.nFontDes = 4;
  858.     gi.kiTextDes = gi.kiCur;
  859.     gi.nAlignDes = 0x6 | 0x8 /* Center | Bottom */;
  860.     MetaSelect();
  861.     MetaTextOut(x, y+5*gi.nScale, 1);
  862.     MetaWord(WFromBB(szAspectFont[asp-1], 0));
  863.     return;
  864.   }
  865. #endif
  866.   if (us.fParallel && asp <= aOpp)
  867.     asp += cAspect;
  868.   if ((gi.nScale & 1) || !szDrawAspect2[asp][0])
  869.     DrawTurtle(szDrawAspect[asp], x, y);
  870.   else {
  871.     gi.nScale >>= 1;
  872.     DrawTurtle(szDrawAspect2[asp], x, y);  /* Special hi-res aspect glyphs. */
  873.     gi.nScale <<= 1;
  874.   }
  875. }
  876.  
  877.  
  878. /* Convert a string segment to a positive number, updating the string to  */
  879. /* point beyond the number chars. Return 1 if the string doesn't point to */
  880. /* a numeric value. This is used by the DrawTurtle() routine to extract   */
  881. /* motion vector quantities from draw strings, e.g. the "12" in "U12".    */
  882.  
  883. int NFromPch(str)
  884. CONST char **str;
  885. {
  886.   int num = 0, i = 0;
  887.  
  888.   loop {
  889.     if (**str < '0' || **str > '9')
  890.       return num > 0 ? num : (i < 1 ? 1 : 0);
  891.     num = num*10+(**str)-'0';
  892.     (*str)++;
  893.     i++;
  894.   }
  895. }
  896.  
  897.  
  898. /* This routine is used to draw complicated objects composed of lots of line */
  899. /* segments on the screen, such as all the glyphs and coastline pieces. It   */
  900. /* is passed in a string of commands defining what to draw in relative       */
  901. /* coordinates. This is a copy of the format of the BASIC draw command found */
  902. /* in PC's. For example, "U5R10D5L10" means go up 5 dots, right 10, down 5,  */
  903. /* and left 10 - draw a box twice as wide as it is high.                     */
  904.  
  905. void DrawTurtle(sz, x0, y0)
  906. CONST char *sz;
  907. int x0, y0;
  908. {
  909.   int i, x, y, deltax, deltay;
  910.   bool fBlank, fNoupdate;
  911.   char chCmd;
  912.  
  913.   gi.xTurtle = x0; gi.yTurtle = y0;
  914.   while (chCmd = ChCap(*sz)) {
  915.     sz++;
  916.  
  917.     /* 'B' prefixing a command means just move the cursor, and don't draw. */
  918.  
  919.     if (fBlank = (chCmd == 'B')) {
  920.       chCmd = ChCap(*sz);
  921.       sz++;
  922.     }
  923.  
  924.     /* 'N' prefixing a command means don't update cursor when done drawing. */
  925.  
  926.     if (fNoupdate = (chCmd == 'N')) {
  927.       chCmd = ChCap(*sz);
  928.       sz++;
  929.     }
  930.  
  931.     /* Here we process the eight directional commands. */
  932.  
  933.     switch (chCmd) {
  934.     case 'U': deltax =  0; deltay = -1; break;      /* Up    */
  935.     case 'D': deltax =  0; deltay =  1; break;      /* Down  */
  936.     case 'L': deltax = -1; deltay =  0; break;      /* Left  */
  937.     case 'R': deltax =  1; deltay =  0; break;      /* Right */
  938.     case 'E': deltax =  1; deltay = -1; break;      /* NorthEast */
  939.     case 'F': deltax =  1; deltay =  1; break;      /* SouthEast */
  940.     case 'G': deltax = -1; deltay =  1; break;      /* SouthWest */
  941.     case 'H': deltax = -1; deltay = -1; break;      /* NorthWest */
  942.     default: PrintError("Bad draw.");       /* Shouldn't happen. */
  943.     }
  944.     x = gi.xTurtle;
  945.     y = gi.yTurtle;
  946.     i = NFromPch(&sz)*gi.nScale;    /* Figure out how far to draw. */
  947.     if (fBlank) {
  948.       gi.xTurtle += deltax*i;
  949.       gi.yTurtle += deltay*i;
  950.     } else {
  951.       gi.xTurtle += deltax*i;
  952.       gi.yTurtle += deltay*i;
  953.       DrawLine(x, y, gi.xTurtle, gi.yTurtle);
  954.       if (fNoupdate) {
  955.         gi.xTurtle = x;
  956.         gi.yTurtle = y;
  957.       }
  958.     }
  959.   }
  960. }
  961. #endif /* GRAPH */
  962.  
  963. /* xgeneral.c */
  964.